`include "../codigo/Alu.v"
`include "../codigo/Shifter.v"


module Execute (

    input               clock,
    input               reset,

    // Decode
    input               id_ex_selalushift,
    input               id_ex_selimregb,
    input               id_ex_selsarega,
    input        [2:0]  id_ex_aluop,
    input               id_ex_unsig,
    input        [1:0]  id_ex_shiftop,
    input        [4:0]  id_ex_shiftamt,
    input       [31:0]  id_ex_rega,
    input        [2:0]  id_ex_msm,
    input        [2:0]  id_ex_msl,
    input               id_ex_readmem,
    input               id_ex_writemem,
    input               id_ex_mshw,
    input               id_ex_lshw,
    input       [31:0]  id_ex_regb,
    input       [31:0]  id_ex_imedext,
    input       [31:0]  id_ex_proximopc,
    input        [2:0]  id_ex_selwsource,
    input        [4:0]  id_ex_regdest,
    input               id_ex_writereg,
    input               id_ex_writeov,
    
    // Forwarding
    output      [31:0]  ex_fw_wbvalue,
    output              ex_fw_writereg,

    // Fetch
    output reg          ex_if_stall,

    // Memory
    output reg   [2:0]  ex_mem_msm,
    output reg   [2:0]  ex_mem_msl,
    output reg          ex_mem_readmem,
    output reg          ex_mem_writemem,
    output reg          ex_mem_mshw,
    output reg          ex_mem_lshw,
    output reg  [31:0]  ex_mem_regb,
    output reg   [2:0]  ex_mem_selwsource,
    output reg   [4:0]  ex_mem_regdest,
    output reg          ex_mem_writereg,
    output reg  [31:0]  ex_mem_aluout, 
    output reg  [31:0]  ex_mem_wbvalue 

    );

    
    // alu(a, b, aluop, aluout, unsig, compout, overflow)
    wire [31:0] aluout;
    wire [0:0] compout;
    wire [0:0] aluov;
    Alu alu(id_ex_rega, id_ex_regb, id_ex_aluop,
        aluout, id_ex_unsig, compout, aluov);


    // shifter(value_in, shiftop, shiftamt, result)
    wire [31:0] result;
    Shifter shifter(id_ex_regb, id_ex_shiftop,
        id_ex_shiftamt, result);


    // ALU / shifter output
    wire [31:0] mux_alusft;
    reg [31:0] mux_alusft_reg;

    assign mux_alusft = mux_alusft_reg;

    always @(negedge(clock)) begin
        if (id_ex_selalushift) begin
            mux_alusft_reg = result;
        end else begin
            mux_alusft_reg = aluout;
        end
    end


    // Register B / sign extended value
    wire [31:0] mux_imregb;
    reg [31:0] mux_imregb_reg;

    assign mux_imregb = mux_imregb_reg;

    always @(negedge(clock)) begin
        if (id_ex_selimregb == 1'b0) begin
            mux_imregb_reg = id_ex_regb;
        end else begin
            mux_imregb_reg = id_ex_imedext;
        end
    end


    // Register A / shift amount
    wire [4:0] mux_sarega;
    reg [4:0] mux_sarega_reg;

    assign mux_sarega = mux_sarega_reg;

    always @(negedge(clock)) begin
        if (id_ex_selsarega == 1'b0) begin
            mux_sarega_reg = id_ex_rega[4:0];
        end else begin
            mux_sarega_reg = id_ex_shiftamt;
        end
    end


    // Writeback
    wire [31:0] mux_wbvalue;
    reg [31:0] mux_wbvalue_reg;

    assign mux_wbvalue = mux_wbvalue_reg;

    always @(negedge(clock)) begin

        case (id_ex_selwsource)

            3'b000: begin
                mux_wbvalue_reg = mux_alusft;
            end

            3'b010: begin
                mux_wbvalue_reg = id_ex_imedext;
            end

            3'b011: begin
                mux_wbvalue_reg = id_ex_proximopc;
            end

            3'b100: begin
                mux_wbvalue_reg = (32'b0 | compout);
            end

            default: begin
                mux_wbvalue_reg = 32'bz;
            end

        endcase

    end


    // Writeback value
    assign ex_fw_wbvalue = mux_wbvalue;
    assign ex_fw_writereg = id_ex_writereg;


    // If under memory access read/write
    always @(negedge(clock)) begin
       
        if (id_ex_readmem | id_ex_writemem) begin
            ex_if_stall = 1'b1;
        end else begin
            ex_if_stall = 1'b0;
        end

    end

    
    // Resets values
    always @(negedge(reset)) begin

        ex_mem_msm = 3'b0;
        ex_mem_msl = 3'b0;
        ex_mem_readmem = 1'b0;
        ex_mem_writemem = 1'b0;
        ex_mem_mshw = 1'b0;
        ex_mem_lshw = 1'b0;
        ex_mem_regb = 32'b0;
        ex_mem_selwsource = 3'b0;
        ex_mem_regdest = 5'b0;  
        ex_mem_writereg = 1'b0;
        ex_mem_aluout = 32'b0;
        ex_mem_wbvalue = 32'b0;

    end


    // Values transferred to the memory unit
    always @(negedge(clock)) begin
        
        ex_mem_msm = id_ex_msm;
        ex_mem_msl = id_ex_msl;
        ex_mem_readmem = id_ex_readmem;
        ex_mem_writemem = id_ex_writemem;
        ex_mem_mshw = id_ex_mshw;
        ex_mem_lshw = id_ex_lshw;
        ex_mem_regb = id_ex_regb;
        ex_mem_selwsource = id_ex_selwsource;
        ex_mem_regdest = id_ex_regdest;
        ex_mem_writereg = (aluov | id_ex_writeov) & id_ex_writereg;
        ex_mem_aluout = mux_alusft;
        ex_mem_wbvalue = mux_wbvalue;

    end

endmodule
